package com.execjar;

import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.ArrayUtil;
import com.execjar.fun.FunctionParse;
import com.execjar.fun.FunctionPound;
import com.execjar.utils.CalculUtils;
import com.execjar.utils.TcpToolUtils;
import com.google.common.collect.EvictingQueue;
import lombok.Data;
import lombok.experimental.Accessors;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;

import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Slf4j(topic = "p")
public class AppRunComm {
    private static final int LIST_SIZE = 8;

    public static void mains(String[] args) {
        //192.168.50.52
        String ip = ArrayUtil.isEmpty(args) ? "192.168.50.62" : args[0];

        PoundCalParam poundParam = new PoundCalParam().setLoopTime(180l * 1000 + System.currentTimeMillis());
        // 窗口队列
        EvictingQueue<Integer> queue = EvictingQueue.create(LIST_SIZE);

        while (true) {
            try {
                byte[] tcpRead = funcPound.apply(ip, String.valueOf(5302), 50);
                Integer readPound = funParseRead.apply(tcpRead);
                poundParam.setPound(String.valueOf(readPound));
                queue.add(readPound);
                printInfo(ip, queue);
                if (calcQueue(queue)) {
                    break;
                }
            } catch (Exception e) {
                log.error("err", e);
                poundParam.incrNetErrNum();

            }
            if (poundParam.getErrNumNet() >= 3) {
                System.out.println("错误次数超过3次");
                break;
            }
            // 1秒是根据实际情况测试出来的
            ThreadUtil.sleep(1500, TimeUnit.MILLISECONDS);
            if (System.currentTimeMillis() > poundParam.getLoopTime().longValue()) {
                System.out.println("读取超时");
                break;
            }
        }


        //如果读取的时间小于1,认为只读取1次

        Integer max = CalculUtils.mode(queue.stream().collect(Collectors.toList()));
        log.info("地磅[{}]计算完成[{}]", ip, max);


    }

    private static void printInfo(String ip, EvictingQueue<Integer> queue) {
        long sum = queue.stream().mapToLong(item -> item.longValue()).sum();
        String join = StringUtils.join(queue.iterator(), ',');
        if (sum > 0) {
            log.info("当前IP[{}]队列中元素:[{}][{}]", ip, queue.size(), join);
        } else {
            System.out.println(ip + "读取到的数据" + join);
        }

    }


    static FunctionParse<byte[], Integer> funParseRead = (readBytes) -> Optional.ofNullable(readBytes)
            .map(item -> {
                String s = new String(item, StandardCharsets.UTF_8);
                //log.info("地磅读取返回[{}]", s);
                return s;
            }).filter(item -> item.length() > 36).map(item -> {
                List<String> collect = Arrays.stream(item.split("\u0003\u0002"))
                        .filter(str -> StringUtils.length(str.trim()) >= 9)
                        .map(str -> StringUtils.substring(str, 2, str.length() - 3))
                        .collect(Collectors.toList());
                //读取到的不是标准，截取2位的第二位
                String parseIntStr = collect.size() >= 2 ? collect.get(1) : "";
                return Integer.parseInt(parseIntStr);
            }).get();
    static FunctionPound<String, String, Integer, byte[]> funcPound = (ip, port, readLen) -> TcpToolUtils.postReAssignByte(ip, Integer.parseInt(port), readLen);

    /**
     * 判断队列中的元素是基本相等
     *
     * @param queue 队列
     * @return true 元素相等，可以停止
     */
    private static boolean calcQueue(EvictingQueue<Integer> queue) {
        List<Integer> intList = queue.stream().sorted(Comparator.comparingInt(Integer::intValue).reversed()).collect(Collectors.toList());
        Integer max = intList.get(0);
        Integer min = intList.get(intList.size() - 1);
        if ((min > 50) && (max - min) <= 200) {
            return true;
        }
        return false;
    }

    /**
     * 地磅读取过程中的控制参数
     */
    @Data
    @Accessors(chain = true)
    private static class PoundCalParam {
        Long loopTime;
        // 记录错误次数
        int errNumNet = 0;
        int errNumParse = 0;
        //读取结果
        String pound = "";
        //是否是网络异常
        Boolean netErr = Boolean.TRUE;

        public void incrNetErrNum() {
            errNumNet++;
        }

        public void incrParseErrNum() {
            errNumParse++;
        }
    }
}
